// $Id: handlers.c 88 2011-10-17 18:00:11Z cedric.shih@gmail.com $
/*
 * Copyright 2011 Cedric Shih (cedric dot shih at gmail dot com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "log.h"

#include "handlers.h"

struct niortsp_handlers *niortsp_handlers_new(void)
{
	struct niortsp_handlers *handlers;

	if (!(handlers = (struct niortsp_handlers *)calloc(
			1, sizeof(*handlers)))) {
		niortsp_error("calloc: %s", strerror(errno));
		goto finally;
	}

	RB_INIT(&handlers->tree);

finally:
	return handlers;
}

void niortsp_handlers_free(struct niortsp_handlers *handlers)
{
	if (!handlers) {
		return;
	}

	niortsp_handlers_clear(handlers);
	free(handlers);
}

struct niortsp_handler_entry *niortsp_handlers_find(
		struct niortsp_handlers *handlers, const char *method)
{
	struct niortsp_handler_entry find;

	find.method = (char *)method;
	return RB_FIND(niortsp_handler_tree, &handlers->tree, &find);
}

int niortsp_handlers_add(struct niortsp_handlers *handlers,
		const char *method, niortsp_handler_cb cb, void *cbarg)
{
	int rc;
	struct niortsp_handler_entry *entry;

	if ((entry = niortsp_handlers_find(handlers, method))) {
		niortsp_error("exists: %s", method);
		rc = EEXIST;
		goto finally;
	}

	if (!(entry = (struct niortsp_handler_entry *)
			calloc(1, sizeof(*entry)))) {
		rc = errno ? errno : -1;
		niortsp_error("calloc: %s", strerror(rc));
		goto finally;
	}

	if (!(entry->method = strdup(method))) {
		rc = errno ? errno : -1;
		niortsp_error("strdup: %s", strerror(rc));
		free(entry);
		goto finally;
	}

	RB_INSERT(niortsp_handler_tree, &handlers->tree, entry);

	entry->cb = cb;
	entry->cbarg = cbarg;
	rc = 0;

finally:
	return rc;
}

void niortsp_handlers_remove(struct niortsp_handlers *handlers,
		struct niortsp_handler_entry *entry)
{
	RB_REMOVE(niortsp_handler_tree, &handlers->tree, entry);

	if (entry->method) {
		free(entry->method);
	}

	free(entry);
}

void niortsp_handlers_clear(struct niortsp_handlers *handlers)
{
	struct niortsp_handler_entry *entry;

	while ((entry = RB_MIN(niortsp_handler_tree, &handlers->tree))) {
		niortsp_handlers_remove(handlers, entry);
	}
}

int niortsp_handler_entry_compare(struct niortsp_handler_entry *a,
		struct niortsp_handler_entry *b)
{
	return (strcmp(a->method, b->method));
}

RB_GENERATE(niortsp_handler_tree, niortsp_handler_entry, entry,
		niortsp_handler_entry_compare);
